{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "# भाग 10: एन्क्रिप्टेड ग्रैडिएंट एग्रीगेशन के साथ फेडरेटेड लर्निंग\n",
    "\n",
    "पिछले कुछ खंडों में, हम कई सरल कार्यक्रमों का निर्माण करके एन्क्रिप्टेड संगणना के बारे में सीख रहे हैं। इस भाग में, हम [Federated Learning Demo of Part 4](https://github.com/OpenMined/PySyft/blob/dev/examples/tutorials/Part%204/%20/%20Federated%20Learning%20-29%20Trusted%20Aggregator.ipynb) पर लौटने वाले हैं, जहां हमारे पास \"विश्वसनीय एग्रीगेटर\" था, जो कई श्रमिकों से मॉडल अपडेट के औसत के लिए जिम्मेदार था।\n",
    "\n",
    "अब हम इस विश्वसनीय एग्रीगेटर को हटाने के लिए एन्क्रिप्टेड कम्प्यूटेशन के लिए अपने नए उपकरणों का उपयोग करेंगे क्योंकि यह आदर्श से कम है क्योंकि यह मानता है कि हम किसी को इस विश्वसनीय जानकारी तक पहुंच के लिए पर्याप्त भरोसेमंद पा सकते हैं। ऐसी स्थिति हर बार नहीं होती है।\n",
    "\n",
    "इस प्रकार, इस नोटबुक में, हम दिखाएंगे कि कैसे SMPC का उपयोग सुरक्षित एकत्रीकरण करने के लिए किया जा सकता है जैसे कि हमें \"विश्वसनीय एग्रीगेटर\" की आवश्यकता नहीं है।\n",
    "\n",
    "लेखक:\n",
    "- Theo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel)\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "nbTranslate का उपयोग करके अनुवादित\n",
    "\n",
    "संपादक:\n",
    "\n",
    "- Urvashi Raheja - Github: [@raheja](https://github.com/raheja)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "# धारा 1: सामान्य फेडरेटेड लर्निंग\n",
    "\n",
    "सबसे पहले, यहां कुछ कोड हैं जो बोस्टन हाउसिंग डेटासेट पर क्लासिक फ़ेडरेटेड लर्निंग करता है। कोड का यह खंड कई खंडों में विभाजित है।\n",
    "\n",
    "### स्थापना"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import TensorDataset, DataLoader\n",
    "\n",
    "class Parser:\n",
    "    \"\"\"Parameters for training\"\"\"\n",
    "    def __init__(self):\n",
    "        self.epochs = 10\n",
    "        self.lr = 0.001\n",
    "        self.test_batch_size = 8\n",
    "        self.batch_size = 8\n",
    "        self.log_interval = 10\n",
    "        self.seed = 1\n",
    "    \n",
    "args = Parser()\n",
    "\n",
    "torch.manual_seed(args.seed)\n",
    "kwargs = {}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## डेटासेट लोड हो रहा है"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('../data/BostonHousing/boston_housing.pickle','rb') as f:\n",
    "    ((X, y), (X_test, y_test)) = pickle.load(f)\n",
    "\n",
    "X = torch.from_numpy(X).float()\n",
    "y = torch.from_numpy(y).float()\n",
    "X_test = torch.from_numpy(X_test).float()\n",
    "y_test = torch.from_numpy(y_test).float()\n",
    "# preprocessing\n",
    "mean = X.mean(0, keepdim=True)\n",
    "dev = X.std(0, keepdim=True)\n",
    "mean[:, 3] = 0. # the feature at column 3 is binary,\n",
    "dev[:, 3] = 1.  # so we don't standardize it\n",
    "X = (X - mean) / dev\n",
    "X_test = (X_test - mean) / dev\n",
    "train = TensorDataset(X, y)\n",
    "test = TensorDataset(X_test, y_test)\n",
    "train_loader = DataLoader(train, batch_size=args.batch_size, shuffle=True, **kwargs)\n",
    "test_loader = DataLoader(test, batch_size=args.test_batch_size, shuffle=True, **kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "en"
   },
   "source": [
    "## Neural  नेटवर्क संरचना"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.fc1 = nn.Linear(13, 32)\n",
    "        self.fc2 = nn.Linear(32, 24)\n",
    "        self.fc3 = nn.Linear(24, 1)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = x.view(-1, 13)\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = self.fc3(x)\n",
    "        return x\n",
    "\n",
    "model = Net()\n",
    "optimizer = optim.SGD(model.parameters(), lr=args.lr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## हुकिंग PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import syft as sy\n",
    "\n",
    "hook = sy.TorchHook(torch)\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n",
    "james = sy.VirtualWorker(hook, id=\"james\")\n",
    "\n",
    "compute_nodes = [bob, alice]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "**श्रमिकों को डेटा भेजें** <br>\n",
    "आमतौर पर उनके पास पहले से ही होता है, यह केवल डेमो उद्देश्यों के लिए है जो हम इसे मैन्युअल रूप से भेजते हैं"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_distributed_dataset = []\n",
    "\n",
    "for batch_idx, (data,target) in enumerate(train_loader):\n",
    "    data = data.send(compute_nodes[batch_idx % len(compute_nodes)])\n",
    "    target = target.send(compute_nodes[batch_idx % len(compute_nodes)])\n",
    "    train_distributed_dataset.append((data, target))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## प्रशिक्षण फॅन्क्शॅन"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(epoch):\n",
    "    model.train()\n",
    "    for batch_idx, (data,target) in enumerate(train_distributed_dataset):\n",
    "        worker = data.location\n",
    "        model.send(worker)\n",
    "\n",
    "        optimizer.zero_grad()\n",
    "        # update the model\n",
    "        pred = model(data)\n",
    "        loss = F.mse_loss(pred.view(-1), target)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        model.get()\n",
    "            \n",
    "        if batch_idx % args.log_interval == 0:\n",
    "            loss = loss.get()\n",
    "            print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format(\n",
    "                epoch, batch_idx * data.shape[0], len(train_loader),\n",
    "                       100. * batch_idx / len(train_loader), loss.item()))\n",
    "        \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## परीक्षण फॅन्क्शॅन"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test():\n",
    "    model.eval()\n",
    "    test_loss = 0\n",
    "    for data, target in test_loader:\n",
    "        output = model(data)\n",
    "        test_loss += F.mse_loss(output.view(-1), target, reduction='sum').item() # sum up batch loss\n",
    "        pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability\n",
    "        \n",
    "    test_loss /= len(test_loader.dataset)\n",
    "    print('\\nTest set: Average loss: {:.4f}\\n'.format(test_loss))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## मॉडल का प्रशिक्षण"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t = time.time()\n",
    "\n",
    "for epoch in range(1, args.epochs + 1):\n",
    "    train(epoch)\n",
    "\n",
    "    \n",
    "total_time = time.time() - t\n",
    "print('Total', round(total_time, 2), 's')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## गणना प्रदर्शन"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "# धारा 2: एन्क्रिप्टेड एकत्रीकरण जोड़ना\n",
    "\n",
    "अब हम इस उदाहरण को एन्क्रिप्शन का उपयोग करते हुए समुच्चय को थोड़ा संशोधित करने जा रहे हैं। `train()` फ़ंक्शन में कोड का मुख्य टुकड़ा जो वास्तव में अलग है वह 1 या 2 लाइनों का है, जिसे हम इंगित करेंगे। फिलहाल, आइए अपने डेटा को फिर से संसाधित करें और बॉब और एलिस के लिए एक मॉडल को इनिशियलाइज़ करें।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "remote_dataset = (list(),list())\n",
    "\n",
    "train_distributed_dataset = []\n",
    "\n",
    "for batch_idx, (data,target) in enumerate(train_loader):\n",
    "    data = data.send(compute_nodes[batch_idx % len(compute_nodes)])\n",
    "    target = target.send(compute_nodes[batch_idx % len(compute_nodes)])\n",
    "    remote_dataset[batch_idx % len(compute_nodes)].append((data, target))\n",
    "\n",
    "def update(data, target, model, optimizer):\n",
    "    model.send(data.location)\n",
    "    optimizer.zero_grad()\n",
    "    pred = model(data)\n",
    "    loss = F.mse_loss(pred.view(-1), target)\n",
    "    loss.backward()\n",
    "    optimizer.step()\n",
    "    return model\n",
    "\n",
    "bobs_model = Net()\n",
    "alices_model = Net()\n",
    "\n",
    "bobs_optimizer = optim.SGD(bobs_model.parameters(), lr=args.lr)\n",
    "alices_optimizer = optim.SGD(alices_model.parameters(), lr=args.lr)\n",
    "\n",
    "models = [bobs_model, alices_model]\n",
    "params = [list(bobs_model.parameters()), list(alices_model.parameters())]\n",
    "optimizers = [bobs_optimizer, alices_optimizer]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## हमारे प्रशिक्षण तर्क का निर्माण\n",
    "\n",
    "केवल **वास्तविक** अंतर इस ट्रेन पद्धति के अंदर है। चलो इसे चरण-दर-चरण चलते हैं।\n",
    "\n",
    "### भाग ए: ट्रेन:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is selecting which batch to train on\n",
    "data_index = 0\n",
    "# update remote models\n",
    "# we could iterate this multiple times before proceeding, but we're only iterating once per worker here\n",
    "for remote_index in range(len(compute_nodes)):\n",
    "    data, target = remote_dataset[remote_index][data_index]\n",
    "    models[remote_index] = update(data, target, models[remote_index], optimizers[remote_index])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "### भाग बी: एन्क्रिप्टेड एकत्रीकरण"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a list where we'll deposit our encrypted model average\n",
    "new_params = list()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# iterate through each parameter\n",
    "for param_i in range(len(params[0])):\n",
    "\n",
    "    # for each worker\n",
    "    spdz_params = list()\n",
    "    for remote_index in range(len(compute_nodes)):\n",
    "        \n",
    "        # select the identical parameter from each worker and copy it\n",
    "        copy_of_parameter = params[remote_index][param_i].copy()\n",
    "        \n",
    "        # since SMPC can only work with integers (not floats), we need\n",
    "        # to use Integers to store decimal information. In other words,\n",
    "        # we need to use \"Fixed Precision\" encoding.\n",
    "        fixed_precision_param = copy_of_parameter.fix_precision()\n",
    "        \n",
    "        # now we encrypt it on the remote machine. Note that \n",
    "        # fixed_precision_param is ALREADY a pointer. Thus, when\n",
    "        # we call share, it actually encrypts the data that the\n",
    "        # data is pointing TO. This returns a POINTER to the \n",
    "        # MPC secret shared object, which we need to fetch.\n",
    "        encrypted_param = fixed_precision_param.share(bob, alice, crypto_provider=james)\n",
    "        \n",
    "        # now we fetch the pointer to the MPC shared value\n",
    "        param = encrypted_param.get()\n",
    "        \n",
    "        # save the parameter so we can average it with the same parameter\n",
    "        # from the other workers\n",
    "        spdz_params.append(param)\n",
    "\n",
    "    # average params from multiple workers, fetch them to the local machine\n",
    "    # decrypt and decode (from fixed precision) back into a floating point number\n",
    "    new_param = (spdz_params[0] + spdz_params[1]).get().float_precision()/2\n",
    "    \n",
    "    # save the new averaged parameter\n",
    "    new_params.append(new_param)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "### भाग सी: सफाई"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with torch.no_grad():\n",
    "    for model in params:\n",
    "        for param in model:\n",
    "            param *= 0\n",
    "\n",
    "    for model in models:\n",
    "        model.get()\n",
    "\n",
    "    for remote_index in range(len(compute_nodes)):\n",
    "        for param_index in range(len(params[remote_index])):\n",
    "            params[remote_index][param_index].set_(new_params[param_index])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "## चलो इसे सब एक साथ रखो !!\n",
    "\n",
    "और अब जब हम प्रत्येक चरण को जानते हैं, तो हम इसे एक साथ एक प्रशिक्षण लूप में डाल सकते हैं!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(epoch):\n",
    "    for data_index in range(len(remote_dataset[0])-1):\n",
    "        # update remote models\n",
    "        for remote_index in range(len(compute_nodes)):\n",
    "            data, target = remote_dataset[remote_index][data_index]\n",
    "            models[remote_index] = update(data, target, models[remote_index], optimizers[remote_index])\n",
    "\n",
    "        # encrypted aggregation\n",
    "        new_params = list()\n",
    "        for param_i in range(len(params[0])):\n",
    "            spdz_params = list()\n",
    "            for remote_index in range(len(compute_nodes)):\n",
    "                spdz_params.append(params[remote_index][param_i].copy().fix_precision().share(bob, alice, crypto_provider=james).get())\n",
    "\n",
    "            new_param = (spdz_params[0] + spdz_params[1]).get().float_precision()/2\n",
    "            new_params.append(new_param)\n",
    "\n",
    "        # cleanup\n",
    "        with torch.no_grad():\n",
    "            for model in params:\n",
    "                for param in model:\n",
    "                    param *= 0\n",
    "\n",
    "            for model in models:\n",
    "                model.get()\n",
    "\n",
    "            for remote_index in range(len(compute_nodes)):\n",
    "                for param_index in range(len(params[remote_index])):\n",
    "                    params[remote_index][param_index].set_(new_params[param_index])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test():\n",
    "    models[0].eval()\n",
    "    test_loss = 0\n",
    "    for data, target in test_loader:\n",
    "        output = models[0](data)\n",
    "        test_loss += F.mse_loss(output.view(-1), target, reduction='sum').item() # sum up batch loss\n",
    "        pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability\n",
    "        \n",
    "    test_loss /= len(test_loader.dataset)\n",
    "    print('Test set: Average loss: {:.4f}\\n'.format(test_loss))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t = time.time()\n",
    "\n",
    "for epoch in range(args.epochs):\n",
    "    print(f\"Epoch {epoch + 1}\")\n",
    "    train(epoch)\n",
    "    test()\n",
    "\n",
    "    \n",
    "total_time = time.time() - t\n",
    "print('Total', round(total_time, 2), 's')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "hi"
   },
   "source": [
    "# बधाई हो!!! - समुदाय में शामिल होने का समय!\n",
    "\n",
    "इस नोटबुक ट्यूटोरियल को पूरा करने पर बधाई! यदि आपने इसका आनंद लिया है और एआई और एअर सप्लाई चेन (डेटा) के विकेन्द्रीकृत स्वामित्व के संरक्षण की ओर आंदोलन में शामिल होना चाहते हैं, तो आप निम्न तरीकों से ऐसा कर सकते हैं!\n",
    "\n",
    "### Github पर स्टार PySyft\n",
    "\n",
    "हमारे समुदाय की मदद करने का सबसे आसान तरीका सिर्फ रिपोज अभिनीत है! यह हमारे द्वारा बनाए जा रहे कूल टूल्स के बारे में जागरूकता बढ़ाने में मदद करता है।\n",
    "\n",
    "- [स्टार पायसफ्ट](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### हमारे Slack में शामिल हों!\n",
    "\n",
    "नवीनतम प्रगति पर अद्यतित रहने का सबसे अच्छा तरीका हमारे समुदाय में शामिल होना है! [http://slack.openmined.org](http://slack.openminn.org) पर फॉर्म भरकर आप ऐसा कर सकते हैं\n",
    "\n",
    "### एक कोड परियोजना में शामिल हों!\n",
    "\n",
    "हमारे समुदाय में योगदान करने का सबसे अच्छा तरीका एक कोड योगदानकर्ता बनना है! किसी भी समय आप PySyft Github जारी करने वाले पृष्ठ पर जा सकते हैं और \"Projects\" के लिए फ़िल्टर कर सकते हैं। यह आपको सभी शीर्ष स्तर के टिकट दिखाएगा कि आप किन परियोजनाओं में शामिल हो सकते हैं! यदि आप किसी प्रोजेक्ट से जुड़ना नहीं चाहते हैं, लेकिन आप थोड़ी सी कोडिंग करना चाहते हैं, तो आप \"good first issue\" चिह्नित गिथब मुद्दों की खोज करके अधिक मिनी-प्रोजेक्ट्स की तलाश कर सकते हैं।\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### दान करना\n",
    "\n",
    "यदि आपके पास हमारे कोडबेस में योगदान करने का समय नहीं है, लेकिन फिर भी समर्थन उधार देना चाहते हैं, तो आप हमारे ओपन कलेक्टिव में भी एक बैकर बन सकते हैं। सभी दान हमारी वेब होस्टिंग और अन्य सामुदायिक खर्च जैसे कि हैकाथॉन और मीटअप की ओर जाते हैं!\n",
    "\n",
    "[OpenMined का ओपन कलेक्टिव पेज](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "nbTranslate": {
   "displayLangs": [
    "hi"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "hi",
   "useGoogleTranslate": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
